--*************ANIMATION SUITE****************--
--**************Property of Hxrmn*****************--
--*************Hxrmn@comcast.net*****************--
--DO NOT RE-DISTRIBUTE WITHOUT HXRMN'S CONSENT--
--********************************************--

Animator = {}

function Animator:Employ(player)
	local anim = {}
	setmetatable(anim, self)
	self.__index = self
	self.tracks = {}
	self.player = player;
	self.curTime = 1;
	player:GetTable().animatorMeta = anim;
	self:Initialize()
	self.nextId = 0
	
	return anim
end

function Animator:GetTracks()
	self:CheckTracks()
	return self.tracks
end

function Animator:GetTimelineLength()
	local Max = 0;
	for _,track in pairs(self.tracks) do
		local t = track:GetLength()
		if(t > Max) then
			Max = t;
		end
	end
	return Max
end

function Animator:Initialize()
	self.currentTime = 0;
	self.animType = AS_PLAY_LOOP;
	self.playDirection = 1
	self.status = AS_STATUS_IDLE
	self.controls = Controls:TieToAnimator(self)
	self.frameDelay = .06;
	self.nextFrameTime = CurTime()
	AS_Msg("Animator Create For '" .. self.player:Nick() .. "'",self,AS_MESSAGE_CHAT)
end

function Animator:SetTrackIds()
	local id = 1
	for k,v in pairs (self.tracks) do
		v:SetId(id)
		id = id + 1
	end
end

function Animator:SetStatus(v)
	self.status = v;
	--AS_Msg("Status = " .. self.status .. "\n",self,AS_MESSAGE_CHAT)
end

function Animator:Dismiss()
	for k,v in pairs(self.tracks) do
		v:Destroy()
	end
	self.tracks = nil
	self.player = nil
end

function Animator:AddTrack(entity)
	self:Stop()
	if(!entity.hasTrack) then
		local track = Track:Create(self)
		if(track:SetEntity(entity)) then
			self.nextId = self.nextId + 1
			track:SetId(self.nextId)
			table.insert(self:GetTracks(),track)
			if(CLIENT) then AS_TrackAdded(track) end
			AS_Msg("Track Added",self,AS_MESSAGE_CHAT)
			entity.hasTrack = true
		else
			track = nil
			AS_Msg("Could not create track: Entity invalid.",self,AS_MESSAGE_CHAT)
		end
	else
		AS_Msg("Could not create track: Entity Already Has A Track.",self,AS_MESSAGE_CHAT)
	end
	--self:SetTrackIds()
end

function Animator:RemoveTrackById(id)
	for _,track in pairs(self.tracks) do
		if(track:GetId() == id) then
			if(CLIENT) then AS_TrackRemoved(track) end
			track:Destroy();
		end
	end
end

function Animator:RemoveTrack(entity)
	self:Stop()
	if(entity.hasTrack) then
		Msg("Track Found!\n")
		for k,v in pairs(self:GetTracks()) do
			if(entity != nil and entity:IsValid()) then
				if(v:GetEntity() == entity) then
					Msg("Track Match!\n")
					if(CLIENT) then AS_TrackRemoved(v) end
					v:Destroy()	
				end
			else
				Msg("Ent Missing!\n")
				if(CLIENT) then AS_TrackRemoved(v) end
				v:Destroy()				
			end
		end
	end
	--self:SetTrackIds()
end

function Animator:GetPlayer()
	return self.player
end

function Animator:GetTime()
	return self.curTime
end

function Animator:SetTime(t,noClamp)
	self.curTime = t
	local psave = {}
	if(self.status == AS_STATUS_IDLE) then
		for k,phys in pairs (self:GetAllPhysicsObjects()) do
			psave[k] = phys:IsMoveable()
			phys:EnableGravity(false)
			phys:EnableMotion(true)
		end
	end
	if(!noClamp) then
		self.curTime = math.Clamp(self.curTime,0,self:GetTimelineLength())
	end
	for _,track in pairs(self.tracks) do
		track:SetTime(self.curTime)
	end
	if(self.status == AS_STATUS_IDLE) then
		for k,phys in pairs (self:GetAllPhysicsObjects()) do
			phys:EnableGravity(true)
			phys:EnableMotion(psave[k])
		end
	end
	if(!noClamp) then
		if(self.curTime == self:GetTimelineLength()) then return false end
		if(self.curTime == 0) then return false end
	end
	return true
end

function Animator:AdvanceTime(amount,noClamp)
	return self:SetTime(self.curTime + amount,noClamp)
end

function Animator:CaptureFrame()
	AS_Msg("Frame Captured",self,AS_MESSAGE_CHAT)
	for _,track in pairs(self.tracks) do
		if(track:IsArmed()) then
			track:CaptureKeyFrame()
		end
	end
end

function Animator:ApplyFrames()
	for _,track in pairs(self.tracks) do
		track:ApplyKeyFrame()
	end
end

function Animator:ToggleTrack(id)
	id = tonumber(id)
	local txt = "off"
	for _,track in pairs(self.tracks) do
		if(track:GetId() == id) then
			track:SetArmed(!track:IsArmed())
			if(track:IsArmed()) then txt = "on" end
		end
	end
end

function Animator:DeleteArmedTracks()
	for i,track in pairs(self.tracks) do
		if(track:IsArmed()) then
			AS_Msg("Removed Track: " .. track:GetId(),self,AS_MESSAGE_CHAT)
			if(CLIENT) then AS_TrackRemoved(track) end
			track:Destroy()
			self:Stop()
		end
	end
	for i=1, #self.tracks do
		local t = self.tracks[i]
		if(t != nil and t.valid == false) then
			table.remove(self.tracks,i)
		end
	end
	--self:SetTrackIds()
end

function Animator:HasActiveTracks()
	for _,track in pairs(self.tracks) do
		if(track:IsArmed()) then
			return true;
		end
	end
	return false;
end

function Animator:CheckTracks()
	local changed = false
	local count = #self.tracks
	for i=1, #self.tracks do
		local t = self.tracks[i]
		if(t != nil) then
			t:GetEntity();
			if(t.valid == false) then
				if(CLIENT) then AS_TrackRemoved(t) end
				t:Destroy()
				table.remove(self.tracks,i)
			end
		else
			table.remove(self.tracks,i)
		end
	end
	--for i=1, count do
	--	local t = self.tracks[i]
	--	if(t != nil and t.valid == false) then
	--		if(CLIENT) then AS_TrackRemoved(t) end
	--		changed = true
	--		self:Stop()
	--		table.remove(self.tracks,i)
	--	end
	--end
	--if(changed) then self:SetTrackIds() end
end

function Animator:GetAllPhysicsObjects()
	local objects = {}
	for k,v in pairs(self:GetTracks()) do
		table.Add(objects,v:GetPhysicsObjects())
	end
	return objects
end

function Animator:Play()
	self:SetStatus(AS_STATUS_PLAY)
	for _,phys in pairs (self:GetAllPhysicsObjects()) do
		phys:EnableGravity(false)
		phys:EnableMotion(true)
	end
	self.nextFrameTime = 0
end

function Animator:Record()
	self:SetStatus(AS_STATUS_RECORD)
	self.nextFrameTime = 0
end

function Animator:Stop(override)
	self.playDirection = 1
	self:SetStatus(AS_STATUS_IDLE)
	for _,phys in pairs (self:GetAllPhysicsObjects()) do
		phys:EnableGravity(true)
		phys:EnableMotion(false)
	end
	if(!override) then
		self:SetTime(0)
	end
	self.nextFrameTime = 0
end

function Animator:Think()
	self:CheckTracks()
	if(SERVER) then
		if(self.status == AS_STATUS_PLAY) then
		if(#self.tracks == 0) then self:Stop() return end
			if(self.nextFrameTime < CurTime()) then
				local ended = !self:AdvanceTime(self.playDirection)
				if(ended) then
					if(self.animType == AS_PLAY_ONCE) then
						self:Stop(true)
					elseif(self.animType == AS_PLAY_LOOP) then
						self:SetTime(1)
					elseif(self.animType == AS_PLAY_PINGPONG) then
						self.playDirection = self.playDirection * -1
					end
				end
				self.nextFrameTime = CurTime() + self.frameDelay
			end
		end
	end
	if(CLIENT) then
		if(self.status == AS_STATUS_PLAY) then
			if(#self.tracks == 0) then self:Stop() return end
		end
		if(self.status == AS_STATUS_RECORD) then
			if(self.nextFrameTime < CurTime()) then
				AS_Msg("Attempting To Capture Frame...",self,AS_MESSAGE_CHAT)
				self:GetControls():CaptureAndAdvance()
				self.nextFrameTime = CurTime() + self.frameDelay
			end
		end
	end
	--AS_Msg(self.status,self,AS_MESSAGE_CHAT)
end

function Animator:DudeWtf()
	if(HxrmnsCode == true and Stolen == true) then
		Hxrmn():GetFoot(RIGHT_FOOT):Kick(YourAss)
	end
end

function Animator:GetControls()
	return self.controls;
end

function Animator:GetAllEntities()
	local ents = {}
	for k,v in pairs(self.tracks) do
		local ent = v:GetEntity()
		if(ent != nil) then
			table.insert(ents,ent)
		end
	end
	return ents
end